import React, { useCallback, useState, useEffect } from 'react';
import { Table, message } from 'antd';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import DragBodyRow from '../DragBodyRow';
import update from 'immutability-helper';
import { arrayToTree, isParent, isSomeLevel } from '../DragBodyRow/util';

import type { TableProps } from 'antd/es/table';

interface TreeTable extends TableProps<any> {
  /**
   * @default id
   */
  idKey?: string;

  /**
   * @default pid
   */
  pidKey?: string;

  /**
   * 启动节点下，限制只同层级拖拽
   * @default false
   */
  enableDragSomeLevel?: boolean;

  /**
   * 拖拽结束事件
   */
  draged?: (newList: Record<string, any>[]) => void;
}

const Index: React.FC<TreeTable> = ({
  dataSource = [],
  idKey = 'id',
  pidKey = 'pid',
  enableDragSomeLevel = false,
  ...opt
}) => {
  const [data, setData] = useState<Record<string, any>[]>([]);

  const components = {
    body: {
      row: (opt: any) => <DragBodyRow {...opt} />,
    },
  };

  const moveRow = useCallback(
    (dragIndex: number, hoverIndex: number, dragKey, hoverKey) => {
      const dragObject = data.find((ele) => ele[idKey] === dragKey) ?? {},
        hoverObject = data.find((ele) => ele[idKey] === hoverKey) ?? {};
      let newData = data.filter((ele) => ele[idKey] !== dragKey),
        insertIndex = 0;
      if (dragObject[pidKey] !== hoverObject[pidKey]) {
        // 判断拖动的元素是否是目标元素的父级
        if (isParent(data, hoverObject, dragObject, idKey, pidKey)) {
          message.error('父级不能拖拽到子级下');
          return;
        }

        if (
          enableDragSomeLevel &&
          !isSomeLevel(data, hoverObject, dragObject, idKey, pidKey)
        ) {
          message.error('拖拽元素节点，必须与目标节点同级');
          return;
        }

        dragObject.pid = hoverObject.pid;
      }

      newData.forEach((ele, index) => {
        if (ele[idKey] === hoverKey) {
          if (dragIndex > hoverIndex) {
            insertIndex = index;
          } else {
            insertIndex = index + 1;
          }
        }
      });

      const updateData = update(newData, {
        $splice: [[insertIndex, 0, dragObject]],
      });

      setData(updateData);
      opt.draged?.(updateData);
    },
    [data],
  );

  useEffect(() => {
    if (!dataSource) return;
    setData((dataSource ?? []) as Record<string, any>[]);
  }, [dataSource]);

  return (
    <DndProvider backend={HTML5Backend}>
      <Table
        {...opt}
        dataSource={arrayToTree(
          data,
          opt.childrenColumnName || 'children',
          idKey,
          pidKey,
        )}
        components={components}
        onRow={(_, index) => {
          const attr = {
            index,
            moveRow,
          };
          return attr as React.HTMLAttributes<any>;
        }}
      />
    </DndProvider>
  );
};

export default Index;
